// SPDX-License-Identifier: GPL-2.0
/*
 * BUDDY GFP: GFP_USER
 *
 * (C) 2024.05.12 <buddy.zhang@aliyun.com>
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mm.h>

#define DEV_NAME		"BiscuitOS"

/* PRIVATE DATA */
struct BiscuitOS_data {
	char *buffer;
};

static ssize_t BiscuitOS_write(struct file *filp, const char __user *buf,
			size_t len, loff_t *offset)
{
	struct BiscuitOS_data bdata;
	struct page *page;

	/* ALLOC MEMORY FOR USER */
	page = alloc_page(GFP_USER);
	if (!page)
		return -ENOMEM;

	/* KERNEL DIRECT ACCESS */
	bdata.buffer = (char *)page_to_virt(page);

	/* COPY DATA FROM USERSPACE */
	if (copy_from_user(bdata.buffer, buf, len)) {
		printk(KERN_ERR "UNABLE COPY DATA FROM USER.\n");
		return -EINVAL;
	}
	printk("DATA FROM USERSPACE: %s\n", bdata.buffer);
	__free_page(page);

	return len;
}

static struct file_operations BiscuitOS_fops = {
	.owner		= THIS_MODULE,
	.write		= BiscuitOS_write,
};

static struct miscdevice BiscuitOS_drv = {
	.minor	= MISC_DYNAMIC_MINOR,
	.name	= DEV_NAME,
	.fops	= &BiscuitOS_fops,
};

static int __init BiscuitOS_init(void)
{
	misc_register(&BiscuitOS_drv);
	return 0;
}

static void __exit BiscuitOS_exit(void)
{
	misc_deregister(&BiscuitOS_drv);
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("BUDDY ALLOCATOR ON BiscuitOS");
